﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Drawing.Drawing2D;

namespace Djs.Common.Components
{
    public class GPainter
    {
        #region DrawRadiance
        public static void DrawRadiance(Graphics graphics, Point center, Color centerColor)
        {
            DrawRadiance(graphics, center, null, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Point center, Rectangle? clipBounds, Color centerColor)
        {
            Rectangle bounds = center.CreateRectangleFromCenter(new Size(45, 30));
            DrawRadiance(graphics, bounds, clipBounds, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Point center, Size size, Color centerColor)
        {
            DrawRadiance(graphics, center, size, null, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Point center, Size size, Rectangle? clipBounds, Color centerColor)
        {
            Rectangle bounds = center.CreateRectangleFromCenter(size);
            DrawRadiance(graphics, bounds, clipBounds, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Rectangle bounds, Color centerColor)
        {
            DrawRadiance(graphics, bounds, null, centerColor);
        }
        public static void DrawRadiance(Graphics graphics, Rectangle bounds, Rectangle? clipBounds, Color centerColor)
        {
            System.Drawing.Drawing2D.GraphicsPath p = new System.Drawing.Drawing2D.GraphicsPath();
            p.AddEllipse(bounds);
            using (System.Drawing.Drawing2D.PathGradientBrush b = new System.Drawing.Drawing2D.PathGradientBrush(p))
            {
                b.CenterColor = centerColor;
                b.CenterPoint = bounds.Center();
                b.SurroundColors = new Color[] { Color.Transparent };
                if (clipBounds.HasValue)
                {
                    Region clip = graphics.Clip;
                    graphics.SetClip(clipBounds.Value);
                    graphics.FillEllipse(b, bounds);
                    graphics.Clip = clip;
                }
                else
                {
                    graphics.FillEllipse(b, bounds);
                }
            }
        }
        #endregion
        #region DrawArea
        /// <summary>
        /// Draw button base (background and border, by state)
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="color"></param>
        /// <param name="state"></param>
        /// <param name="opacity"></param>
        public static void DrawAreaBase(Graphics graphics, Rectangle bounds, Color color, GInteractiveState state, Orientation orientation, Point? point, Int32? opacity)
        {
            if (bounds.Width <= 0 || bounds.Height <= 0) return;
            DrawAreaBase(graphics, bounds, color, state, orientation, point, opacity, 0);
        }
        /// <summary>
        /// Draw button base (background and border, by state)
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="color"></param>
        /// <param name="state"></param>
        /// <param name="opacity"></param>
        public static void DrawAreaBase(Graphics graphics, Rectangle bounds, Color color, GInteractiveState state, Orientation orientation, Point? point, Int32? opacity, int roundCorner)
        {
            if (bounds.Width <= 0 || bounds.Height <= 0) return;

            int roundX = roundCorner;
            int roundY = roundCorner;
            GraphicsPath gp = CreatePathRoundRectangle(bounds, roundX, roundY);

            Color colorBgr1, colorBgr2;
            ModifyBackColorByState(state, color, out colorBgr1, out colorBgr2, opacity);
            using (Brush br = CreateBrushForBackground(bounds, orientation, point, state, colorBgr1, colorBgr2))
            {
                graphics.FillPath(br, gp);
            }
        }
        #endregion
        #region DrawButton
        /// <summary>
        /// Draw button base (background and border, by state)
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="color"></param>
        /// <param name="state"></param>
        /// <param name="opacity"></param>
        public static void DrawButtonBase(Graphics graphics, Rectangle bounds, Color color, GInteractiveState state, Orientation orientation, Point? point, Int32? opacity)
        {
            DrawButtonBase(graphics, bounds, color, state, orientation, 2, point, opacity);
        }
        /// <summary>
        /// Draw button base (background and border, by state)
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="color"></param>
        /// <param name="state"></param>
        /// <param name="opacity"></param>
        public static void DrawButtonBase(Graphics graphics, Rectangle bounds, Color color, GInteractiveState state, Orientation orientation, int roundCorner, Point? point, Int32? opacity)
        {
            if (bounds.Width <= 0 || bounds.Height <= 0) return;
            bounds = bounds.ResizeBy(0, 0, -1, -1);
            using (GPainter.GraphicsUseSmooth(graphics))
            {
                int roundX = roundCorner;
                int roundY = roundCorner;
                GraphicsPath gp = CreatePathRoundRectangle(bounds, roundX, roundY);
                
                Color colorBgr1, colorBgr2;
                ModifyBackColorByState(state, color, out colorBgr1, out colorBgr2, opacity);
                using (Brush br = CreateBrushForBackground(bounds, orientation, point, state, colorBgr1, colorBgr2))
                {
                    graphics.FillPath(br, gp);
                }

                Color colorPen = Color.DimGray;
                Color colorPen1, colorPen2;
                ModifyForeColorByState(state, colorPen, out colorPen1, out colorPen2, opacity);
                using (Pen pen = new Pen(colorPen))
                {
                    GraphicsPath gp1 = CreatePathRoundRectangle(bounds, roundX, roundY, p => (p == RelativePosition.BottomLeft || p == RelativePosition.Left || p == RelativePosition.LeftTop || p == RelativePosition.Top));
                    pen.Color = colorPen1;
                    graphics.DrawPath(pen, gp1);

                    GraphicsPath gp2 = CreatePathRoundRectangle(bounds, roundX, roundY, p => (p == RelativePosition.TopRight || p == RelativePosition.Right || p == RelativePosition.RightBottom || p == RelativePosition.Bottom));
                    pen.Color = colorPen2;
                    graphics.DrawPath(pen, gp2);
                }
            }
        }
        public static Brush CreateBrushForBackground(Rectangle bounds, Orientation orientation, GInteractiveState state, Color color)
        {
            Color colorBgr1, colorBgr2;
            ModifyBackColorByState(state, color, out colorBgr1, out colorBgr2, null);
            return CreateBrushForBackground(bounds, orientation, null, state, colorBgr1, colorBgr2);
        }
        public static Brush CreateBrushForBackground(Rectangle bounds, Orientation orientation, GInteractiveState state, Color color, Point? point, Int32? opacity)
        {
            Color colorBgr1, colorBgr2;
            ModifyBackColorByState(state, color, out colorBgr1, out colorBgr2, opacity);
            return CreateBrushForBackground(bounds, orientation, point, state, colorBgr1, colorBgr2);
        }

        /// <summary>
        /// Get brush for button
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="relativePoint"></param>
        /// <param name="state"></param>
        /// <param name="colorBgr1"></param>
        /// <param name="colorBgr2"></param>
        /// <returns></returns>
        public static Brush CreateBrushForBackground(Rectangle bounds, Orientation orientation, Point? relativePoint, GInteractiveState state, Color colorBgr1, Color colorBgr2)
        {
            if (relativePoint.HasValue && (state == GInteractiveState.Mouse || state == GInteractiveState.LeftDown || state == GInteractiveState.LeftDrag || state == GInteractiveState.RightDown || state == GInteractiveState.RightDrag))
            {
                GraphicsPath gp = new GraphicsPath();
                gp.AddEllipse(CreateEllipseRectangle(bounds, 4));
                PathGradientBrush gb = new PathGradientBrush(gp);
                gb.CenterColor = colorBgr1;
                gb.CenterPoint = bounds.Location.Add(relativePoint.Value);
                gb.SurroundColors = new Color[] { colorBgr2 };
                return gb;
            }
            float angle = (orientation == Orientation.Horizontal ? 90f : 0f);
            return new LinearGradientBrush(bounds, colorBgr1, colorBgr2, angle);
        }
        #endregion
        #region DrawString
        /// <summary>
        /// Draw a text to specified area
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="text"></param>
        /// <param name="color"></param>
        /// <param name="font"></param>
        /// <param name="alignment"></param>
        public static void DrawString(Graphics graphics, Rectangle bounds, string text, Color color, Font font, ContentAlignment alignment)
        {
            _DrawString(graphics, bounds, text, null, color, font, alignment, null);
        }
        /// <summary>
        /// Draw a text to specified area
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="text"></param>
        /// <param name="color"></param>
        /// <param name="font"></param>
        /// <param name="alignment"></param>
        public static void DrawString(Graphics graphics, Rectangle bounds, string text, Color color, Font font, ContentAlignment alignment, Action<Rectangle> drawBackground)
        {
            _DrawString(graphics, bounds, text, null, color, font, alignment, drawBackground);
        }
        /// <summary>
        /// Draw a text to specified area
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="text"></param>
        /// <param name="color"></param>
        /// <param name="font"></param>
        /// <param name="alignment"></param>
        public static void DrawString(Graphics graphics, Rectangle bounds, string text, Brush brush, Font font, ContentAlignment alignment)
        {
            _DrawString(graphics, bounds, text, brush, null, font, alignment, null);
        }
        /// <summary>
        /// Draw a text to specified area
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="text"></param>
        /// <param name="color"></param>
        /// <param name="font"></param>
        /// <param name="alignment"></param>
        public static void DrawString(Graphics graphics, Rectangle bounds, string text, Brush brush, Font font, ContentAlignment alignment, Action<Rectangle> drawBackground)
        {
            _DrawString(graphics, bounds, text, brush, null, font, alignment, drawBackground);
        }
        /// <summary>
        /// Draw a text to specified area
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="text"></param>
        /// <param name="color"></param>
        /// <param name="font"></param>
        /// <param name="alignment"></param>
        private static void _DrawString(Graphics graphics, Rectangle bounds, string text, Brush brush, Color? color, Font font, ContentAlignment alignment, Action<Rectangle> drawBackground)
        {
            if (String.IsNullOrEmpty(text)) return;
            if (bounds.Width <= 0 || bounds.Height <= 0) return;
            StringFormat sf = new StringFormat( StringFormatFlags.LineLimit);

            SizeF textSize = graphics.MeasureString(text, font, bounds.Width, sf);
            Rectangle textArea = textSize.AlignTo(bounds, alignment, true);
            if (drawBackground != null)
                drawBackground(textArea);

            if (brush != null)
                graphics.DrawString(text, font, brush, textArea, sf);
            else if (color.HasValue)
            {
                using (SolidBrush br = new SolidBrush(color.Value))
                {
                    graphics.DrawString(text, font, br, textArea, sf);
                }
            }
            else
                graphics.DrawString(text, font, SystemBrushes.ControlText, textArea, sf);
        }
        #endregion
        #region DrawAxis
        /// <summary>
        /// Paint axis background
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bounds"></param>
        /// <param name="orientation"></param>
        /// <param name="enabled"></param>
        /// <param name="state"></param>
        /// <param name="color"></param>
        /// <param name="morph"></param>
        public static void DrawAxisBackground(Graphics graphics, Rectangle bounds, Orientation orientation, bool enabled, GInteractiveState state, Color color, float morph)
        {
            if (!enabled)
                state = GInteractiveState.None;
            else if (state == GInteractiveState.LeftDrag)
                state = GInteractiveState.LeftDown;
            GPainter.DrawAreaBase(graphics, bounds, color, state, orientation, null, null, 0);
        }
        #endregion
        #region ModifyColorByState
        /// <summary>
        /// Modify background colors by current interactive state.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="color"></param>
        /// <param name="color1"></param>
        /// <param name="color2"></param>
        public static void ModifyBackColorByState(GInteractiveState state, Color color, out Color color1, out Color color2)
        {
            ModifyBackColorByState(state, color, out color1, out color2, null);
        }
        /// <summary>
        /// Modify background colors by current interactive state.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="color"></param>
        /// <param name="color1"></param>
        /// <param name="color2"></param>
        public static void ModifyBackColorByState(GInteractiveState state, Color color, out Color color1, out Color color2, Int32? opacity)
        {
            switch (state)
            {
                case GInteractiveState.None:
                    color1 = color.Morph(Color.White, 0.15f);
                    color2 = color.Morph(Color.DimGray, 0.25f);
                    break;
                case GInteractiveState.Mouse:
                    color1 = color.Morph(Color.White, 0.25f);
                    color2 = color.Morph(Color.DimGray, 0.30f);
                    break;
                case GInteractiveState.LeftDown:
                case GInteractiveState.RightDown:
                    color1 = color.Morph(Color.DimGray, 0.40f);
                    color2 = color.Morph(Color.White, 0.20f);
                    break;
                case GInteractiveState.LeftDrag:
                case GInteractiveState.RightDrag:
                    color1 = color.Morph(Color.DimGray, 0.15f);
                    color2 = color.Morph(Color.White, 0.35f);
                    break;
                default:
                    color1 = color;
                    color2 = color;
                    break;
            }
            ModifyColorByOpacity(opacity, ref color1, ref color2);
        }
        /// <summary>
        /// Modify foreground colors by current interactive state.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="color"></param>
        /// <param name="color1"></param>
        /// <param name="color2"></param>
        public static void ModifyForeColorByState(GInteractiveState state, Color color, out Color color1, out Color color2)
        {
            ModifyForeColorByState(state, color, out color1, out color2, null);
        }
        /// <summary>
        /// Modify foreground colors by current interactive state.
        /// </summary>
        /// <param name="state"></param>
        /// <param name="color"></param>
        /// <param name="color1"></param>
        /// <param name="color2"></param>
        public static void ModifyForeColorByState(GInteractiveState state, Color color, out Color color1, out Color color2, Int32? opacity)
        {
            switch (state)
            {
                case GInteractiveState.None:
                    color1 = color.Morph(Color.DimGray, 0.45f);
                    color2 = color.Morph(Color.White, 0.65f);
                    break;
                case GInteractiveState.Mouse:
                    color1 = color.Morph(Color.DimGray, 0.45f);
                    color2 = color.Morph(Color.White, 0.65f);
                    break;
                case GInteractiveState.LeftDown:
                case GInteractiveState.RightDown:
                    color1 = color.Morph(Color.White, 0.65f);
                    color2 = color.Morph(Color.DimGray, 0.45f);
                    break;
                case GInteractiveState.LeftDrag:
                case GInteractiveState.RightDrag:
                    color1 = color.Morph(Color.White, 0.65f);
                    color2 = color.Morph(Color.DimGray, 0.45f);
                    break;
                default:
                    color1 = color;
                    color2 = color;
                    break;
            }
            ModifyColorByOpacity(opacity, ref color1, ref color2);
        }
        /// <summary>
        /// Modify colors by specified opacity
        /// </summary>
        /// <param name="opacity"></param>
        /// <param name="color1"></param>
        /// <param name="color2"></param>
        public static void ModifyColorByOpacity(int? opacity, ref Color color1, ref Color color2)
        {
            if (opacity.HasValue)
            {
                color1 = Color.FromArgb(opacity.Value, color1);
                color2 = Color.FromArgb(opacity.Value, color2);
            }
        }
        #endregion
        #region CreatePath
        #region RoundRectangle
        /// <summary>
        /// Create GraphicsPath containing rounded rectangle.
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="roundX"></param>
        /// <param name="roundY"></param>
        /// <returns></returns>
        public static GraphicsPath CreatePathRoundRectangle(Rectangle bounds, int roundX, int roundY)
        {
            return CreatePathRoundRectangle(bounds, roundX, roundY, null, null);
        }
        /// <summary>
        /// Create GraphicsPath containing rounded rectangle.
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="roundX"></param>
        /// <param name="roundY"></param>
        /// <returns></returns>
        public static GraphicsPath CreatePathRoundRectangle(Rectangle bounds, int roundX, int roundY, Action<GraphicsPath, RelativePosition, Point, Point> lineHandler)
        {
            return CreatePathRoundRectangle(bounds, roundX, roundY, null, lineHandler);
        }
        /// <summary>
        /// Create GraphicsPath containing rounded rectangle.
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="roundX"></param>
        /// <param name="roundY"></param>
        /// <returns></returns>
        public static GraphicsPath CreatePathRoundRectangle(Rectangle bounds, int roundX, int roundY, Func<RelativePosition, bool> partSelector)
        {
            return CreatePathRoundRectangle(bounds, roundX, roundY, partSelector, null);
        }
        /// <summary>
        /// Create GraphicsPath containing rounded rectangle.
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="roundX"></param>
        /// <param name="roundY"></param>
        /// <returns></returns>
        public static GraphicsPath CreatePathRoundRectangle(Rectangle bounds, int roundX, int roundY, Func<RelativePosition, bool> partSelector, Action<GraphicsPath, RelativePosition, Point, Point> lineHandler)
        {
            GraphicsPath gp = new GraphicsPath();

            int rxr = roundX;
            int rxd = 2 * rxr;
            int ryr = roundY;
            int ryd = 2 * ryr;
            int total = 8;
            bool rounded = (rxd > 0 && ryd > 0 && bounds.Width > rxd && bounds.Height > ryd);
            if (!rounded)
            {
                rxr = 0;
                rxd = 0;
                ryr = 0;
                ryd = 0;
                total = 4;
            }

            int sbx = 0;
            int sby = 0;
            int px0 = bounds.X;
            int px1 = bounds.X + rxr;
            int px2 = bounds.Right - sbx - rxr;
            int px3 = bounds.Right - sbx;
            int py0 = bounds.Y;
            int py1 = bounds.Y + ryr;
            int py2 = bounds.Bottom - sby - ryr;
            int py3 = bounds.Bottom - sby;

            bool isBroken = false;
            int partCount = 0;

            if (rounded && _CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.LeftTop, RelativePosition.TopLeft))
                gp.AddArc(px0, py0, rxd, ryd, 180f, 90f);

            if (_CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.Top))
                _CreatePathRoundRectangleAddLine(gp, RelativePosition.Top, px1, py0, px2, py0, lineHandler);

            if (rounded && _CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.TopRight, RelativePosition.RightTop))
                gp.AddArc(px3 - rxd, py0, rxd, ryd, 270f, 90f);

            if (_CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.Right))
                _CreatePathRoundRectangleAddLine(gp, RelativePosition.Right, px3, py1, px3, py2, lineHandler);

            if (rounded && _CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.RightBottom, RelativePosition.BottomRight))
                gp.AddArc(px3 - rxd, py3 - ryd, rxd, ryd, 0f, 90f);

            if (_CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.Bottom))
                _CreatePathRoundRectangleAddLine(gp, RelativePosition.Bottom, px2, py3, px1, py3, lineHandler);

            if (rounded && _CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.BottomLeft, RelativePosition.LeftBottom))
                gp.AddArc(px0, py3 - ryd, rxd, ryd, 90f, 90f);

            if (_CreatePathRoundRectangleIsSelected(gp, partSelector, ref isBroken, ref partCount, RelativePosition.Left))
                _CreatePathRoundRectangleAddLine(gp, RelativePosition.Left, px0, py2, px0, py1, lineHandler);

            if (partCount == total)
                gp.CloseFigure();

            return gp;
        }
        private static bool _CreatePathRoundRectangleIsSelected(GraphicsPath gp, Func<RelativePosition, bool> partSelector, ref bool isBroken, ref int partCount, params RelativePosition[] positions)
        {
            bool selected = false;
            if (partSelector != null)
            {
                foreach (RelativePosition position in positions)
                {
                    if (partSelector(position))
                    {
                        selected = true;
                        break;
                    }
                }
            }
            else
            {
                selected = true;
            }

            if (selected)
            {
                partCount++;
                if (isBroken)
                {
                    gp.StartFigure();
                    isBroken = false;
                }
            }
            else
            {
                isBroken = true;
            }

            return selected;
        }
        private static void _CreatePathRoundRectangleAddLine(GraphicsPath gp, RelativePosition position, int x0, int y0, int x1, int y1, Action<GraphicsPath, RelativePosition, Point, Point> lineHandler)
        {
            if (lineHandler != null)
                lineHandler(gp, position, new Point(x0, y0), new Point(x1, y1));
            else
                gp.AddLine(x0, y0, x1, y1);
        }
    	#endregion
    	#region RoundRectangleWithArrow
        /// <summary>
        /// Create GraphicsPath containing rounded rectangle with arrow to specific point (tooltip, label, comics, etc).
        /// </summary>
        /// <param name="bounds"></param>
        /// <param name="roundX"></param>
        /// <param name="roundY"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        public static GraphicsPath CreatePathRoundRectangleWithArrow(Rectangle bounds, int roundX, int roundY, Point target)
        {
            RelativePosition position = FindTargetRelativePosition(target, bounds, roundX + 7, roundY + 7);
            return CreatePathRoundRectangle(bounds, roundX, roundY, (gp, lp, p0, p1) => _AddLineWithArrow(gp, lp, p0, p1, position));
        }
        private static void _AddLineWithArrow(GraphicsPath gp, RelativePosition linePosition, Point point0, Point point1, RelativePosition arrowPosition)
        {
            gp.AddLine(point0, point1);
        }
        /// <summary>
        /// Detect target point position relative to specified bounds.
        /// Position is Inside (target is inside bounds), or one from nine positions.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="bounds"></param>
        /// <returns></returns>
        public static RelativePosition FindTargetRelativePosition(Point target, Rectangle bounds)
        {
            return FindTargetRelativePosition(target, bounds, 0, 0);
        }
        /// <summary>
        /// Detect target point position relative to specified bounds, which can be reduced by specified border (x,y).
        /// Position is Inside (target is inside bounds), or one from nine positions.
        /// </summary>
        /// <param name="target"></param>
        /// <param name="bounds"></param>
        /// <param name="borderX"></param>
        /// <param name="borderY"></param>
        /// <returns></returns>
        public static RelativePosition FindTargetRelativePosition(Point target, Rectangle bounds, int borderX, int borderY)
        {
            if (bounds.Contains(target)) return RelativePosition.Inside;
            
            // Coordinates of X (left, right) and Y (top, bottom), of bounds + borders:
            int xl = bounds.Left + borderX;
            int xr = bounds.Right - 1 - borderX;
            if (xl > xr)
            {
                xl = bounds.Left + bounds.Width / 2;
                xr = xl;
            }
            int yt = bounds.Top + borderY;
            int yb = bounds.Bottom - 1 - borderY;
            if (yt > yb)
            {
                yt = bounds.Top + bounds.Height / 2;
                yb = yt;
            }

            // Cordinates of point, distances point from borders:
            int x = target.X;
            int y = target.Y;
            int dxl = xl - x;
            int dxr = x - xr;
            int dyt = yt - y;
            int dyb = y - yb;
            if (dxl > 0)
            {   // On Left:
                if (dyt > 0) return ((dxl >= dyt) ? RelativePosition.LeftTop : RelativePosition.TopLeft);                // Left, Top:    when dx[Left] >= dy[Top], then LeftTop, otherwise TopLeft
                if (dyb > 0) return ((dxl >= dyb) ? RelativePosition.LeftBottom : RelativePosition.BottomLeft);          // Left, Bottom: when dx[Left] >= dy[Bottom], then LeftBottom, otherwise BottomLeft
                return RelativePosition.Left;
            }

            if (dxr > 0)
            {   // On Right:
                if (dyt > 0) return ((dxr >= dyt) ? RelativePosition.RightTop : RelativePosition.TopRight);              // Right, Top:    when dx >= dy[Top], then RightTop, otherwise TopRight
                if (dyb > 0) return ((dxr >= dyb) ? RelativePosition.RightBottom : RelativePosition.BottomRight);        // Right, Bottom: when dx >= dy[Bottom], then RightBottom, otherwise BottomRight
                return RelativePosition.Right;
            }

            if (dyt > 0) return RelativePosition.Top;        // On Top (only Top; because other is solved: LeftTop,TopLeft,TopRight,RightTop)
            if (dyb > 0) return RelativePosition.Bottom;     // On Bottom (dtto)
            return RelativePosition.None;
        }
    	#endregion
        /// <summary>
        /// Returns a rectangle of the ellipse, which is circumscribed by the specified inner area, and distance from edge of inner area.
        /// </summary>
        /// <param name="innerArea"></param>
        /// <param name="distance"></param>
        /// <returns></returns>
        public static Rectangle CreateEllipseRectangle(Rectangle innerArea, int distance)
        {
            double ew = (double)innerArea.Width;
            if (ew < 10d) ew = 10d;
            double eh = (double)innerArea.Height;
            if (eh < 10d) eh = 10d;
            double a = (double)(ew > eh ? ew : eh);                  // Max (Width, Height): create a square above the larger of the two dimensions,
            double d = Math.Sqrt(2d * a * a) + (double)distance;     //  which creates a diagonal diameter of the circle circumscribed over a square. Increase diameter by 6px.
            double ex = d * ew / a;                                  // Dimension of ellipse on X axis, Y axis:
            double ey = d * eh / a;
            return innerArea.Center().CreateRectangleFromCenter((int)ex, (int)ey);
        }
        #endregion
        #region Set Graphics, restore previous state
        /// <summary>
        /// Prepare Graphics for smooth drawing (curve, text), quality smooth rendering.
        /// Return its original state, can be restored after drawing:  
        /// var state = Painter.GraphicsSetSmooth(graphics); any drawing...;  graphics.Restore(state);
        /// Restore of state is not requierd.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static GraphicsState GraphicsSetSmooth(Graphics graphics)
        {
            GraphicsState state = graphics.Save();
            _GraphicsSetSmooth(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for text drawing (lines, text), fast rendering lines and quality rendering of texts.
        /// Return its original state, can be restored after drawing:  
        /// var state = Painter.GraphicsSetSmooth(graphics); any drawing...;  graphics.Restore(state);
        /// Restore of state is not requierd.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static GraphicsState GraphicsSetText(Graphics graphics)
        {
            GraphicsState state = graphics.Save();
            _GraphicsSetText(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for sharp sharp (rectangles), fast rendering.
        /// Return its original state, can be restored after drawing:  
        /// var state = Painter.GraphicsSetSharp(graphics); any drawing...;  graphics.Restore(state);
        /// Restore of state is not requierd.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static GraphicsState GraphicsSetSharp(Graphics graphics)
        {
            GraphicsState state = graphics.Save();
            _GraphicsSetSharp(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for smooth drawing (curve, text), quality smooth rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSmooth(Graphics graphics)
        {
            IDisposable state = new GraphicsStateRestore(graphics);
            _GraphicsSetSmooth(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for smooth drawing (curve, text), quality smooth rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// Set specified rectangle as Clip region to Graphics. On Dispose returns original Clip region.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSmooth(Graphics graphics, Rectangle setClip)
        {
            IDisposable state = new GraphicsStateRestore(graphics, setClip);
            graphics.SetClip(setClip);
            _GraphicsSetSmooth(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for text drawing (lines, text), fast rendering lines and quality rendering of texts.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseText(Graphics graphics)
        {
            IDisposable state = new GraphicsStateRestore(graphics);
            _GraphicsSetText(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for text drawing (lines, text), fast rendering lines and quality rendering of texts.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSmooth(graphics) { any drawing... }.
        /// Set specified rectangle as Clip region to Graphics. On Dispose returns original Clip region.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseText(Graphics graphics, Rectangle setClip)
        {
            IDisposable state = new GraphicsStateRestore(graphics, setClip);
            graphics.SetClip(setClip);
            _GraphicsSetText(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for sharp sharp (rectangles), fast rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSharp(graphics) { any drawing... }.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSharp(Graphics graphics)
        {
            IDisposable state = new GraphicsStateRestore(graphics);
            _GraphicsSetSharp(graphics);
            return state;
        }
        /// <summary>
        /// Prepare Graphics for sharp sharp (rectangles), fast rendering.
        /// Return a disposable object, which at its Dispose returns graphics to the original state.
        /// You must use this method in using pattern:
        /// using(Painter.GraphicsUseSharp(graphics) { any drawing... }.
        /// Set specified rectangle as Clip region to Graphics. On Dispose returns original Clip region.
        /// </summary>
        /// <param name="graphics"></param>
        /// <returns></returns>
        public static IDisposable GraphicsUseSharp(Graphics graphics, Rectangle setClip)
        {
            IDisposable state = new GraphicsStateRestore(graphics, setClip);
            graphics.SetClip(setClip);
            _GraphicsSetSharp(graphics);
            return state;
        }
        /// <summary>
        /// Set graphic to smooth mode
        /// </summary>
        /// <param name="graphics"></param>
        private static void _GraphicsSetSmooth(Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        }
        /// <summary>
        /// Set graphic to text mode
        /// </summary>
        /// <param name="graphics"></param>
        private static void _GraphicsSetText(Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        }
        /// <summary>
        /// Set graphic to sharp mode
        /// </summary>
        /// <param name="graphics"></param>
        private static void _GraphicsSetSharp(Graphics graphics)
        {
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
        }
        #region class GraphicsStateRestore : Disposable pattern for graphics.Save(), Set, use and Restore on Dispose
        /// <summary>
        /// GraphicsStateRestore : Disposable pattern for graphics.Save(), set, use and Restore on Dispose
        /// </summary>
        private class GraphicsStateRestore : IDisposable
        {
            public GraphicsStateRestore(Graphics graphics)
            {
                this._Graphics = graphics;
                this._State = graphics.Save();
            }
            public GraphicsStateRestore(Graphics graphics, Rectangle setClip)
            {
                this._Graphics = graphics;
                this._State = graphics.Save();
                this._Clip = setClip;
                this._OriginClip = graphics.Clip;
            }
            /// <summary>
            /// Graphics (via WeakReference)
            /// </summary>
            private Graphics _Graphics
            {
                set
                {
                    this.__Graphics = (value == null ? null : new WeakReference(value));
                }
                get
                {
                    if (!this._HasGraphics == null) return null;
                    return this.__Graphics.Target as Graphics;
                }
            }
            /// <summary>
            /// true when WeakReference to Graphics is valid
            /// </summary>
            private bool _HasGraphics
            {
                get { return (this.__Graphics != null && this.__Graphics.IsAlive && this.__Graphics.Target is Graphics); }
            }
            private WeakReference __Graphics;
            private GraphicsState _State;
            private Rectangle? _Clip;
            private Region _OriginClip;
            void IDisposable.Dispose()
            {
                if (this._State != null && this._HasGraphics)
                {
                    Graphics graphics = this._Graphics;
                    graphics.Restore(this._State);
                    if (this._Clip.HasValue)
                    {
                        if (this._OriginClip != null)
                            graphics.SetClip(this._OriginClip, CombineMode.Replace);
                        else
                            graphics.ResetClip();
                    }
                }
                this._Graphics = null;
                this._State = null;
                this._OriginClip = null;
                this._Clip = null;
            }
        }
        #endregion
        #endregion
        #region Static property, containing standardised graphic tools
        public static Brush InteractiveClipBrushForState(GInteractiveState state)
        {
            switch (state)
            {
                case GInteractiveState.None: return InteractiveClipStandardBrush;
                case GInteractiveState.Mouse: return InteractiveClipMouseBrush;
                case GInteractiveState.LeftDown: return InteractiveClipDownBrush;
                case GInteractiveState.RightDown: return InteractiveClipDownBrush;
                case GInteractiveState.LeftDrag: return InteractiveClipDragBrush;
                case GInteractiveState.RightDrag: return InteractiveClipDragBrush;
            }
            return InteractiveClipStandardBrush;
        }
        public static Brush InteractiveClipStandardBrush
        {
            get
            {
                if (_InteractiveClipStandardBrush == null)
                    _InteractiveClipStandardBrush = new SolidBrush(InteractiveClipStandardColor);
                return _InteractiveClipStandardBrush;
            }
        } private static Brush _InteractiveClipStandardBrush;
        public static Brush InteractiveClipMouseBrush
        {
            get
            {
                if (_InteractiveClipMouseBrush == null)
                    _InteractiveClipMouseBrush = new SolidBrush(InteractiveClipMouseColor);
                return _InteractiveClipMouseBrush;
            }
        } private static Brush _InteractiveClipMouseBrush;
        public static Brush InteractiveClipDownBrush
        {
            get
            {
                if (_InteractiveClipDownBrush == null)
                    _InteractiveClipDownBrush = new SolidBrush(InteractiveClipDownColor);
                return _InteractiveClipDownBrush;
            }
        } private static Brush _InteractiveClipDownBrush;
        public static Brush InteractiveClipDragBrush
        {
            get
            {
                if (_InteractiveClipDragBrush == null)
                    _InteractiveClipDragBrush = new SolidBrush(InteractiveClipDragColor);
                return _InteractiveClipDragBrush;
            }
        } private static Brush _InteractiveClipDragBrush;
        public static Color InteractiveClipStandardColor { get { return Color.DimGray; } }
        public static Color InteractiveClipMouseColor { get { return Color.BlueViolet; } }
        public static Color InteractiveClipDownColor { get { return Color.Black; } }
        public static Color InteractiveClipDragColor { get { return Color.Blue; } }
        #endregion
    }
    /// <summary>
    /// Data pro kreslení ticků na ose
    /// </summary>
    public class GPainterDrawAxisTick : IDisposable
    {
        #region Konstrukce, property, Dispose
        public GPainterDrawAxisTick(Rectangle bounds, AxisOrientation orientation, bool verticalText)
        {
            this.Pen = new Pen(Color.Black);
            this.Brush = new SolidBrush(Color.Black);
            this.TickFontT = new Font(SystemFonts.DefaultFont.Name, 9f, FontStyle.Bold);
            this.TickFontS = new Font(SystemFonts.DefaultFont.Name, 8f, FontStyle.Regular);
            this.StringFormat = new StringFormat(StringFormatFlags.NoClip);

            this.Bounds = bounds;
            this.Orientation = orientation;
            this.VerticalText = verticalText;

            this._CalcInternal();
        }
        /// <summary>
        /// Vypočte interní souřadnice podle Bounds a Orientation
        /// </summary>
        private void _CalcInternal()
        {
            switch (this.Orientation)
            {
                case AxisOrientation.Top:
                case AxisOrientation.Bottom:
                    this.AxisLength = this.Bounds.Width;
                    this.AxisWidth = this.Bounds.Height;
                    break;
                case AxisOrientation.LeftDown:
                case AxisOrientation.LeftUp:
                case AxisOrientation.RightDown:
                case AxisOrientation.RightUp:
                    this.AxisLength = this.Bounds.Height;
                    this.AxisWidth = this.Bounds.Width;
                    break;
                default:
                    this.AxisLength = this.Bounds.Width;
                    this.AxisWidth = this.Bounds.Height;
                    break;
            }

            int wd = 14;
            if (this.IsVertical && !this.VerticalText)
                wd = 42;
            int len = this.AxisWidth - wd;
            this.InitialTickLength = this._CalcLength(len, 5, 37);
            this.TitleTickLength = this._CalcLength(this.InitialTickLength - 14, 5, 30);
            this.SubTitleTickLength = this._CalcLength(this.TitleTickLength - 3, 4, 25);
            this.SignificantTickLength = this._CalcLength(this.SubTitleTickLength - 2, 3, 22);
            this.RegularTickLength = this._CalcLength(this.SignificantTickLength - 2, 2, 20);
        }
        /// <summary>
        /// Zarovná délku do daných mezí
        /// </summary>
        /// <param name="len"></param>
        /// <param name="min"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        private int _CalcLength(int len, int min, int max)
        {
            if (len < min) return min;
            if (len > max) return max;
            return len;
        }
        /// <summary>
        /// Pero pro kreslení linek
        /// </summary>
        public Pen Pen { get; private set; }
        /// <summary>
        /// Výplň pro texty
        /// </summary>
        public SolidBrush Brush { get; private set; }
        /// <summary>
        /// Font pro psaní SubTitle
        /// </summary>
        public Font TickFontT { get; private set; }
        /// <summary>
        /// Font pro psaní SubTitle a Initial
        /// </summary>
        public Font TickFontS { get; private set; }
        /// <summary>
        /// Font pro psaní aktuálního ticku
        /// </summary>
        protected Font TickFont { get; private set; }
        /// <summary>
        /// Formar pro text
        /// </summary>
        public StringFormat StringFormat { get; private set; }
        /// <summary>
        /// Rozměry Axis
        /// </summary>
        public Rectangle Bounds { get; private set; }
        /// <summary>
        /// Orientace
        /// </summary>
        public AxisOrientation Orientation { get; private set; }
        /// <summary>
        /// true = Paint text on vertical axis with vertical font;
        /// false (default) = Paint text in horizontal line on vertical axis
        /// </summary>
        public bool VerticalText { get; private set; }
        /// <summary>
        /// Orientace je vodorovná?
        /// </summary>
        public bool IsHorizontal { get { return (this.Orientation == AxisOrientation.Top || this.Orientation == AxisOrientation.Bottom); } }
        /// <summary>
        /// Orientace je svislá?
        /// </summary>
        public bool IsVertical { get { return (this.Orientation == AxisOrientation.LeftUp || this.Orientation == AxisOrientation.LeftDown || this.Orientation == AxisOrientation.RightUp || this.Orientation == AxisOrientation.RightDown); } }
        /// <summary>
        /// Orientace je vlevo?
        /// </summary>
        public bool IsOnLeft { get { return (this.Orientation == AxisOrientation.LeftUp || this.Orientation == AxisOrientation.LeftDown); } }
        /// <summary>
        /// Orientace je vpravo?
        /// </summary>
        public bool IsOnRight { get { return (this.Orientation == AxisOrientation.RightUp || this.Orientation == AxisOrientation.RightDown); } }
        /// <summary>
        /// Délka osy ve směru v němž osa běží (délka pravítka)
        /// </summary>
        public int AxisLength { get; private set; }
        /// <summary>
        /// Šířka osy, ve směru šířky (šířka pravítka)
        /// </summary>
        public int AxisWidth { get; private set; }
        /// <summary>
        /// Linka textu Initial, na které je posazen text (při orientaci Top je to souřadnice Y měřená od spodu osy)
        /// </summary>
        public int InitialTickLength { get; private set; }
        /// <summary>
        /// Linka textu Title, na které je posazen text (při orientaci Top je to souřadnice Y měřená od spodu osy)
        /// </summary>
        public int TitleTickLength { get; private set; }
        /// <summary>
        /// Linka textu SubTitle, na které je posazen text (při orientaci Top je to souřadnice Y měřená od spodu osy)
        /// </summary>
        public int SubTitleTickLength { get; private set; }
        /// <summary>
        /// Velikost ticků Significant
        /// </summary>
        public int SignificantTickLength { get; private set; }
        /// <summary>
        /// Velikost ticků Regular
        /// </summary>
        public int RegularTickLength { get; private set; }
        /// <summary>
        /// Dispose
        /// </summary>
        void IDisposable.Dispose()
        {
            if (this.Pen != null) this.Pen.Dispose();
            this.Pen = null;

            if (this.Brush != null) this.Brush.Dispose();
            this.Brush = null;

            this.TickFont = null;             // Here is only another reference to object TickFontT or TickFontS

            if (this.TickFontT != null) this.TickFontT.Dispose();
            this.TickFontT = null;

            if (this.TickFontS != null) this.TickFontS.Dispose();
            this.TickFontS = null;

            if (this.StringFormat != null) this.StringFormat.Dispose();
            this.StringFormat = null;
        }
        #endregion
        #region Kreslení ticků
        /// <summary>
        /// Kreslí jeden daný tick na ose, jejíž základní charakteristiky jsou uložené v this
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="tick"></param>
        public void DrawTick(Graphics graphics, SizeAxisTick tick)
        {
            // Line:
            Point point1, point2;
            if (this._CalcLinePoints(tick, out point1, out point2))
                graphics.DrawLine(this.Pen, point1, point2);

            // Text:
            Rectangle textBound;
            if (this._CalcTextBounds(graphics, tick, point1, out textBound))
                graphics.DrawString(tick.Text, this.TickFont, this.Brush, textBound, this.StringFormat);
        }
        /// <summary>
        /// Calculate coordinates of point for tick line on current Bounds and Orientation, for specified Tick.
        /// Set value: point1 is near to text, point2 is near to axis edge.
        /// </summary>
        /// <param name="tick"></param>
        /// <param name="point1"></param>
        /// <param name="point2"></param>
        /// <returns></returns>
        private bool _CalcLinePoints(SizeAxisTick tick, out Point point1, out Point point2)
        {
            Rectangle b = this.Bounds;
            int width = ((tick.TickType == AxisTickType.Title || tick.TickType == AxisTickType.SubTitle) ? 2 : 1);
            int aw = (width <= 1 ? 0 : (width - 1));
            int tickLength = this._GetLineLength(tick.TickType);
            int x1 = 0;
            int x2 = 0;
            int y1 = 0;
            int y2 = 0;
            switch (this.Orientation)
            {
                case AxisOrientation.Top:
                    x1 = b.X + tick.Pixel;
                    x2 = x1;
                    y2 = b.Bottom - 2;
                    y1 = y2 - tickLength;
                    y2 += aw;
                    break;
                case AxisOrientation.LeftUp:
                    x2 = b.Right - 2;
                    x1 = x2 - tickLength;
                    x2 += aw;
                    y1 = b.Bottom - 1 - tick.Pixel;
                    y2 = y1;
                    break;
                case AxisOrientation.LeftDown:
                    x2 = b.Right - 2;
                    x1 = x2 - tickLength;
                    x2 += aw;
                    y1 = b.Y + tick.Pixel;
                    y2 = y1;
                    break;
                case AxisOrientation.RightUp:
                    x2 = b.X + 1;
                    x1 = x2 + tickLength;
                    x1 += aw;
                    y1 = b.Bottom - 1 - tick.Pixel;
                    y2 = y1;
                    break;
                case AxisOrientation.RightDown:
                    x2 = b.X + 1;
                    x1 = x2 + tickLength;
                    x1 += aw;
                    y1 = b.Y + tick.Pixel;
                    y2 = y1;
                    break;
                case AxisOrientation.Bottom:
                    x1 = b.X + tick.Pixel;
                    x2 = x1;
                    y2 = b.Y + 1;
                    y1 = y2 + tickLength;
                    y1 += aw;
                    break;
            }
            this.Pen.Color = (tick.TickType == AxisTickType.RegularTick ? Color.Gray : Color.Black);
            this.Pen.Width = width;

            point1 = new Point(x1, y1);
            point2 = new Point(x2, y2);
            return true;
        }
        /// <summary>
        /// Calculate coordinates of text for label for specified Tick.
        /// Value point1 is line point, near to text.
        /// </summary>
        /// <param name="tick"></param>
        /// <param name="point1"></param>
        /// <param name="textBound"></param>
        /// <returns></returns>
        private bool _CalcTextBounds(Graphics graphics, SizeAxisTick tick, Point point1, out Rectangle textBound)
        {
            textBound = Rectangle.Empty;
            if (!(tick.TickType == AxisTickType.Initial || tick.TickType == AxisTickType.Title || tick.TickType == AxisTickType.SubTitle)) return false;
            if (String.IsNullOrEmpty(tick.Text)) return false;

            this.Brush.Color = (tick.TickType == AxisTickType.SubTitle ? Color.DimGray : Color.Black);
            this.TickFont = (tick.TickType == AxisTickType.SubTitle ? this.TickFontS : this.TickFontT);
            bool verticalText = this.IsVertical && this.VerticalText;
            this.StringFormat.FormatFlags = (verticalText ? StringFormatFlags.NoClip | StringFormatFlags.DirectionVertical : StringFormatFlags.NoClip);

            SizeF size = graphics.MeasureString(tick.Text, this.TickFont, PointF.Empty, this.StringFormat);
            AxisTickAlignment alignment = tick.Alignment;
            int x = point1.X;
            int y = point1.Y;
            int x1 = 0;
            int y1 = 0;
            int tw = (int)(size.Width + 2f);
            int th = (int)(size.Height + 2f);
            switch (this.Orientation)
            {
                case AxisOrientation.Top:
                    x1 = x - _GetAlignment(alignment, tw);
                    y1 = y - th + 1;
                    break;
                case AxisOrientation.LeftUp:
                    x1 = x - tw + 1;
                    y1 = y - _GetAlignment(alignment, th);
                    break;
                case AxisOrientation.LeftDown:
                    x1 = x - tw + 1;
                    y1 = y - _GetAlignment(alignment, th);
                    break;
                case AxisOrientation.RightUp:
                    x1 = x + 1;
                    y1 = y - _GetAlignment(alignment, th);
                    break;
                case AxisOrientation.RightDown:
                    x1 = x + 1;
                    y1 = y - _GetAlignment(alignment, th);
                    break;
                case AxisOrientation.Bottom:
                    x1 = x - _GetAlignment(alignment, tw);
                    y1 = y - 1;
                    break;
            }

            textBound = new Rectangle(new Point(x1, y1), Size.Ceiling(size));
            if (textBound.X < this.Bounds.X) textBound.X = this.Bounds.X;
            if (textBound.Y < this.Bounds.Y) textBound.Y = this.Bounds.Y;

            return true;
        }
        /// <summary>
        /// Returns size alignment: for Begin return 0, for Center return size/2, for End return size.
        /// </summary>
        /// <param name="alignment"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        private int _GetAlignment(AxisTickAlignment alignment, float size)
        {
            switch (alignment)
            {
                case AxisTickAlignment.Begin: return 0;
                case AxisTickAlignment.Center: return (int)(size / 2f);
                case AxisTickAlignment.End: return (int)size;
            }
            return 0;
        }
        /// <summary>
        /// Returns a tick length for specified tick type
        /// </summary>
        /// <param name="tickType"></param>
        /// <returns></returns>
        private int _GetLineLength(AxisTickType tickType)
        {
            switch (tickType)
            {
                case AxisTickType.None: return 0;
                case AxisTickType.PixelTick: return 1;
                case AxisTickType.RegularTick: return this.RegularTickLength;
                case AxisTickType.SignificantTick: return this.SignificantTickLength;
                case AxisTickType.SubTitle: return this.SubTitleTickLength;
                case AxisTickType.Title: return this.TitleTickLength;
                case AxisTickType.Initial: return this.InitialTickLength;
            }
            return 0;
        }
        #endregion
    }
    public enum RelativePosition { None, Inside, TopLeft, Top, TopRight, RightTop, Right, RightBottom, BottomRight, Bottom, BottomLeft, LeftBottom, Left, LeftTop }
}
